﻿using System;
using System.Collections.Generic;
using cubeworld.ScreenManager;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace cubeworld.World
{
    class Collision
    {
        static MouseState _oldState;

        public static Vector3 TryMove(Vector3 add, Vector3 old, List<Block> c)
        {
            Vector3 resultV = old;

            if (IsFree(c, new Vector3(old.X + add.X, old.Y, old.Z)))
                resultV.X += add.X;
            if (IsFree(c, new Vector3(old.X, old.Y + add.Y, old.Z)))
                resultV.Y += add.Y;
            if (IsFree(c, new Vector3(old.X, old.Y, old.Z + add.Z)))
                resultV.Z += add.Z;
            return resultV;
        }

        public static Vector3 TryMove(Vector3 add, Vector3 old, out bool result, List<Block> c)
        {
            Vector3 resultV = old;

            if (IsFree(c, new Vector3(old.X + add.X, old.Y, old.Z)))
                resultV.X += add.X;
            if (IsFree(c, new Vector3(old.X, old.Y + add.Y, old.Z)))
                resultV.Y += add.Y;
            if (IsFree(c, new Vector3(old.X, old.Y, old.Z + add.Z)))
                resultV.Z += add.Z;

            if (resultV == old)
                result = false;
            else
                result = true;

            return resultV;
        }

        public static bool IsFree(List<Block> blocks, Vector3 p)
        {
            bool free = true;
            for (int i = 0; i < blocks.Count; i++)
            {
                if (blocks[i] == null)
                    continue;
                if (blocks[i].visible)
                {
                    if (p.Z > (blocks[i].Position.Z - 0.2f) && p.Z < (blocks[i].Position.Z + 1.0f))
                    {
                        if (p.Y > (blocks[i].Position.Y + 1) && p.Y < (blocks[i].Position.Y + 2))
                        {
                            if (p.X > (blocks[i].Position.X - 0.2f) && p.X < (blocks[i].Position.X + 1.0f))
                            {
                                free = false;
                                break;
                            }
                        }
                    }
                }
            }
            return free;
        }

        public static BoundingBox BlockToBoundingBox(Block block)
        {
            BoundingBox b = new BoundingBox(Vector3.Zero, new Vector3(1, 1, 1));
            Vector3[] corners = b.GetCorners();

            for (int i = 0; i < corners.Length; i++)
            {
                corners[i] = Vector3.Transform(corners[i], Matrix.CreateTranslation(block.Position));
            }

            b = BoundingBox.CreateFromPoints(corners);
            return b;
        }

        public static bool RayIntersectsBlock(Chunk chunk, Ray ray)
        {
            bool free = false;
            for (int i = 0; i < chunk.Blocks.Count; i++)
            {
                if (chunk.Blocks[i].visible)
                {
                    float? result = ray.Intersects(BlockToBoundingBox(chunk.Blocks[i]));
                    if (result.HasValue)
                    {
                        free = true;
                        break;
                    }
                }
            }
            return free;
        }

        public static void GetNearestBlockIntersectsRay(Ray ray, float elapsedTime)
        {
            List<Block> blocksInRay = new List<Block>();
            List<int> blocksInRayChunkID = new List<int>();
            List<int> blocksInRayBlockID = new List<int>();
            List<float?> blocksInRayDistance = new List<float?>();
            if (Mouse.GetState().LeftButton == ButtonState.Released && _oldState.LeftButton == ButtonState.Pressed ||
                Mouse.GetState().RightButton == ButtonState.Released && _oldState.RightButton == ButtonState.Pressed)
            {
                int ci = 0;

                foreach (Chunk c in Data.world.checkableChunks)
                {
                    for (int i = 0; i < c.Blocks.Count; i++)
                    {
                        float? result = ray.Intersects(BlockToBoundingBox(c.Blocks[i]));
                        if (result.HasValue && result <= 3.0f)
                        {
                            blocksInRay.Add(Data.world.checkableChunks[ci].Blocks[i]);
                            blocksInRayChunkID.Add(ci);
                            blocksInRayBlockID.Add(i);
                            blocksInRayDistance.Add(result);
                            break;
                        }
                    }
                    ci++;
                }
            }


            if (Mouse.GetState().LeftButton == ButtonState.Released && _oldState.LeftButton == ButtonState.Pressed)
            {
                float? minFloat = 3.5f;
                int i = 0;
                int x = -1;
                foreach (float? distance in blocksInRayDistance)
                {
                    if (distance < minFloat)
                    {
                        x = i;
                        minFloat = distance;
                    }
                    i++;
                }
                if (x != -1)
                {
                    Block b = Data.world.checkableChunks[blocksInRayChunkID[x]].Blocks[blocksInRayBlockID[x]];
                    b.delete();
                    TryAdd(b.Position.X + 1, b.Position.Y, b.Position.Z);
                    TryAdd(b.Position.X + 1, b.Position.Y + 1, b.Position.Z);
                    TryAdd(b.Position.X + 1, b.Position.Y, b.Position.Z + 1);
                    TryAdd(b.Position.X, b.Position.Y + 1, b.Position.Z);
                    TryAdd(b.Position.X + 1, b.Position.Y + 1, b.Position.Z);
                    TryAdd(b.Position.X, b.Position.Y + 1, b.Position.Z + 1);
                    TryAdd(b.Position.X, b.Position.Y, b.Position.Z + 1);
                    TryAdd(b.Position.X + 1, b.Position.Y, b.Position.Z + 1);
                    TryAdd(b.Position.X, b.Position.Y + 1, b.Position.Z + 1);
                    TryAdd(b.Position.X + 1, b.Position.Y + 1, b.Position.Z + 1);
                }
            }
            else if (Mouse.GetState().RightButton == ButtonState.Released && _oldState.RightButton == ButtonState.Pressed)
            {
            }
            _oldState = Mouse.GetState();
        }

        static void TryAdd(float x, float y, float z)
        {
            try
            {
                Block b;
                b = GetBlockFromPosition(new Vector3(x, y, z));
                if (b != null && b.ID != 0)
                {
                    string bs = GetBlockChunkFromPosition(new Vector3(x, y, z));
                    int ci, i;
                    ci = Convert.ToInt32(bs.Split(',')[0]);
                    i = Convert.ToInt32(bs.Split(',')[1]);
                    Data.world.checkableChunks[ci].Blocks[i].visible = true;
                }
            }
            catch (Exception ex)
            {
                Data.SetScreen(new CrashScreen(ex));
            }
        }

        public static string GetBlockChunkFromPosition(Vector3 p)
        {
            int ci = 0;
            foreach (Chunk c in Data.world.checkableChunks)
            {
                for (int i = 0; i < c.Blocks.Count; i++)
                {
                    if (c.Blocks[i].Position == p)
                    {
                        return ci + "," + i;
                        break;
                    }
                }
                ci++;
            }
            return null;
        }

        public static Block GetBlockFromPosition(Vector3 p)
        {
            List<Block> blocks = new List<Block>();
            foreach (Chunk c in Data.world.checkableChunks)
            {
                blocks.AddRange(c.Blocks);
            }
            Block b = blocks.Find(
                delegate(Block ba) { return ba.Position == p; }
                );

            return b;
        }

        /*
        public static void TrySetBlock(Vector3 p)
        {
        }*/
    }
}